import {arrow, autoUpdate, computePosition, ComputePositionConfig, flip, offset} from '@floating-ui/dom';
import Asserts from "../../lang/core/asserts";


/**
 * 展开一个带边界检测的弹出层
 *
 * @param reference 依附对象
 * @param popover 窗口对象
 * @param triangle 箭头对象
 * @param options 窗口参数设置
 * @return cleanup method
 */
export const popup = (
    reference: HTMLElement,
    popover: HTMLElement,
    triangle: HTMLElement,
    options: Partial<ComputePositionConfig>): () => void => {

    computePosition(reference, popover, options)
        .then(({x, y, placement, middlewareData}) => {

            // popover style
            Object.assign(popover.style, {
                left: `${x}px`,
                top: `${y}px`,
            });

            triangle.setAttribute('data-popper-placement', placement);

            // Accessing the data
            const {x: arrowX, y: arrowY} = middlewareData.arrow;

            // arrow style
            Object.assign(triangle.style, {
                left: arrowX != null ? `${arrowX}px` : '',
                top: arrowY != null ? `${arrowY}px` : '',
                right: '',
                bottom: '',
            });
        });
}

/**
 * 展开一个弹出层，弹出层自适应窗口的变化
 *
 * @param reference 依附对象
 * @param popover 窗口对象
 * @param triangle 箭头对象
 * @param options 窗口参数设置
 * @return cleanup method
 */
export const createPopper = (
    reference: HTMLElement,
    popover: HTMLElement,
    triangle: HTMLElement,
    options: Partial<ComputePositionConfig>): () => void => {

    // push arrow
    options.middleware.push(arrow({element: triangle}));

    // This function will get called repeatedly.
    function updatePosition() {
        popup(reference, popover, triangle, options);
    }

    // Start auto updates.
    return autoUpdate(
        reference,
        popover,
        updatePosition,
    );
}

/**
 * 可复用的气泡窗口
 *
 * 可以随时重新绑定弹出层与依附组件。
 */
export class Popper {
    /**
     * 清理所有的事件侦听
     *
     * 如果当前事件存在，则说明窗口处于活动状态
     */
    public cleanup?: () => void;
    /**
     * 气泡窗口 dom 对象
     */
    public popover: HTMLElement | undefined;
    /**
     * 三角形气泡角标 dom 对象
     */
    public triangle: HTMLElement | undefined;
    /**
     * 气泡窗口依附的 dom 对象
     */
    public reference: HTMLElement | undefined;
    /**
     * 气泡窗口参数设置
     */
    public options: Partial<ComputePositionConfig> = {};

    /**
     * 显示气泡窗口
     */
    public display(): void {
        if (this.popover) {
            // don't worry about the issue of attributes cannot be obtained when displaying.
            this.popover.classList.remove('hidden');
        }
    }

    /**
     * 隐藏气泡窗口
     */
    public hidden(): void {
        if (this.popover) {
            this.popover.classList.add('hidden');
        }
    }

    /**
     * 清除事件侦听，并置空引用
     */
    public clean(): void {
        if (this.cleanup) {
            this.cleanup();
            this.cleanup = null;
        }
    }

    /**
     * 当前气泡窗口是否激活
     */
    public active(): boolean {
        return this.cleanup != null;
    }

    /**
     * 初始化气泡窗口，重新绑定窗口和引用之间的关系
     */
    public create(): () => void {
        this.display();

        // destroy the popover if it exists.
        this.clean();

        // Pass the button, the tooltip, and some options, and Popper will do the
        // magic positioning for you:
        this.cleanup = createPopper(this.reference, this.popover, this.triangle, this.options);
        return this.cleanup;
    }

    /**
     * 默认的样式设计
     */
    public withStyle() {
        Asserts.notBlank(this.popover, 'set popover first!')

        // container style
        this.popover.classList.add('sea-popper');

        // arrow style
        const triangle = document.createElement('div');
        triangle.classList.add('sea-popper__arrow');
        this.popover.append(triangle);
        this.triangle = triangle;
    }

    /**
     * 组件销毁
     *
     * dom 移除，事件销毁
     */
    public destroy() {
        // Remove the floating element from the DOM
        this.popover.remove();

        // Stop auto updates.
        this.clean();
    }

    /**
     * 窗口点击事件，除了触发组件和弹窗组件，任何点击事件都要关闭窗口
     */
    public windowsEvent = (evt: Event): void => {
        if (this.active()) {
            const target: EventTarget = evt.target;
            if (!this.popover.contains(target as Node) && !this.reference?.contains(target as Node)) {
                this.hidden();
            }
        }
    }
}

/**
 * 更加美观的 title 弹出层设计
 *
 *有些容器放不下全部文本，文本内容需要截断后展示；
 *
 *如果需要在弹出层中展示全部内容，可以使用当前设计。
 */
interface SmartTitleType {
    /**
     * 展开浮动层事件
     */
    display: (evt: Event) => void;

    /**
     * 隐藏浮动层事件
     */
    hidden: (evt: Event) => void;
}

const useSmartTitle = (): SmartTitleType => {
    // popover-box
    const div = document.createElement('div');
    div.classList.add('box');
    // div.classList.add('hidden');
    div.classList.add('box-primary');
    div.classList.add('sea-popper');
    document.body.append(div);

    // content-box
    const content = document.createElement('span');
    div.append(content);

    // arrow style
    const triangle = document.createElement('div');
    triangle.classList.add('sea-popper__arrow');
    div.append(triangle);

    const options = {
        placement: 'bottom-start',
        middleware: [
            offset(10),
            flip(),
            arrow({element: triangle})
        ]
    }

    /**
     * 窗口点击事件，除了触发组件和弹窗组件，任何点击事件都要关闭窗口
     */
    const windowsEvent = (evt: Event): void => {
        if (show) {
            const target: EventTarget = evt.target;
            if (!div.contains(target) && !reference.contains(target)) {
                hidden();
            }
        }
    }

    document.body.addEventListener("click", windowsEvent);

    /**
     * 展开浮动层
     *
     * @param evt
     */
    let show = false;
    let reference:HTMLElement = undefined;
    const display = (evt: Event) => {
        const target = evt.target;

        div.classList.remove('hidden');

        show = true;
        if (reference !== target) {
            reference = target;

            // max width setting
            let min_width = evt.target.offsetWidth * 1.2;
            const txt = target.innerText;
            if(txt.length > 30 && min_width < 200){
                min_width = 200;
            }

            div.style.maxWidth =  min_width + 'px';

            content.innerText = target.innerText;

            // 测试版，这里为了节省一丢丢性能，取消 auto-update，在 dom 节点发生变化的时候，有错位的风险
            popup(target, div, triangle, options);
        }
    }

    /**
     * 隐藏浮动层
     */
    const hidden = () => {
        div.classList.add('hidden');
        show = false;
    }

    // 尝试打开一次，解决首次错位的 bug
    popup(document.body, div, triangle, options);
    hidden();

    return {display, hidden};
}

/**
 * 更加美观的 title 设计
 */
export const smartTitle: SmartTitleType = useSmartTitle();

























